home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Snippets / Development Tools & Languages / DTSCPlusLibrary / Sources / CollectionClasses.cp < prev    next >
Encoding:
Text File  |  1993-01-14  |  17.0 KB  |  599 lines  |  [TEXT/MPS ]

  1. /* _________________________________________________________________________________________________________ //
  2.   Copyright © 1992-93 Apple Computer, Inc. All rights reserved.
  3.   Macintosh Developer Technical Support.C++ Macintosh Toolbox Framework.
  4.   Programmer: Kent Sandvik
  5.   Date: 11/7/92
  6.   Revision comments are at the end of this file.
  7.   ---
  8.   The following collection classes are implemented: TLinkedList, TStack, (TQueue, TDeQueue),
  9.   THashTable.
  10.   CollectionClasses.cp contains the collection class member functions. 
  11.   _________________________________________________________________________________________________________ */
  12.  
  13. #ifndef _COLLECTION_
  14. #include "CollectionClasses.h"
  15. #endif
  16.  
  17.  
  18. // _________________________________________________________________________________________________________ //
  19. //    TLINKEDLIST Class definitions.
  20.  
  21. //    CONSTRUCTORS AND DESTRUCTORS
  22. #pragma segment Collections
  23. TLinkedList::TLinkedList(short max)
  24. // Create a Linked list class (max entries pre-defined in a constant in the header file).
  25. {
  26.     fMaxCollectionSize = max;                    // keep track of how many entries we could add to the collection
  27.     fCollectionSize = 0;                        // no entries yet
  28.     // Fix the head and the rest of the pointer hooks.
  29.     fHead = new fNODE;                            // create nodes
  30.     fTail = new fNODE;
  31.     fLastEntry = new fNODE;
  32.     fCurrentNode = new fNODE;
  33.  
  34.     fHead->fNext = fTail;                        // default head points at tail, then we will push in nodes between
  35.     fHead->fPrevious = fHead;                    // bit your own tail
  36.     fTail->fNext = fTail;                        // bite your own tail
  37.     fTail->fPrevious = fHead;                    // hook tail with head (double linked list)
  38.     fTail->fKey = NULL;                            // mark the last entry as NULL
  39.     fHead->fKey = NULL;                            // mark the first entry as NULL
  40.     fLastEntry->fPrevious = fHead;                // hook the ptr node between the head
  41.     fLastEntry->fNext = fTail;                    // …and the tail
  42. }
  43.  
  44.  
  45. #pragma segment Collections
  46. TLinkedList::~TLinkedList()
  47. // Destruct the structures required for the former linked list.
  48. {
  49.     struct fNODE* temp = fHead;                    // create a temp node
  50.  
  51.     while (temp != fTail)
  52.         // while we have entries, delete them from head to tail
  53.         {
  54.             fHead = temp;                        // copy temp node to the head position
  55.             temp = temp->fNext;                    // and make temp point at the next one
  56.             delete fHead;                        // meanwhile delete the head, and continue copying tmp to head
  57.         }
  58. }
  59.  
  60.  
  61. //    MAIN INTERFACES
  62.  
  63. #pragma segment Collections
  64. Boolean TLinkedList::Append(const TItemtype item)
  65. // Append entry to the linked list (at the end).
  66. {
  67.     if (fCollectionSize < fMaxCollectionSize)
  68.     {
  69.         struct fNODE*        temp = new fNODE;    // create a new node
  70.         temp->fKey = item;
  71.  
  72.         // Hook the temp node between the last entry and the tail        
  73.         if (fCollectionSize == 0)                // first entry?
  74.         {
  75.             fHead->fNext = temp;                // hook it just after the head
  76.             temp->fNext = fTail;                // and before the tail
  77.             fLastEntry = temp;                    // and keep track of it!
  78.         }
  79.         else                                    // OK now we will track of the fCurrentNode
  80.             {
  81.                 temp->fNext = fTail;            // stuck it just before the tail
  82.                 fLastEntry->fNext = temp;        // and after the current node
  83.                 fLastEntry = temp;                // and mark it the current node
  84.             }
  85.  
  86.         fCollectionSize++;                        // keep a count on the size of the linked list
  87.         goto AppendOK;
  88.     }
  89.     ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of the LinkedList");
  90.     return false;
  91. AppendOK:return true;
  92. }
  93.  
  94.  
  95. #pragma segment Collections
  96. Boolean TLinkedList::Remove(const TItemtype item)
  97. // Remove defined entry from the linked list.
  98. {
  99.     // First find the item, if found remove it, connect the links and decrease the collection size count
  100.     this->Reset();                                // get back to the first entry
  101.     struct fNODE*            temp = fHead;        // keep a temp pointer to earlier node passed
  102.  
  103.     do                                            // walk along the list    
  104.     {
  105.         if (fCurrentNode->fKey == item)            // found a hit?    
  106.         {
  107.             // yes!
  108.             temp->fNext = fCurrentNode->fNext;    // short circuit the current node
  109.             delete fCurrentNode;                // delete this node
  110.             fCollectionSize--;                    // decreate the collection count
  111.             goto OK;                            // and jump out 
  112.         }
  113.  
  114.         temp = fCurrentNode;                    // keep track of the just visited node
  115.         fCurrentNode = fCurrentNode->fNext;        // …and step forward
  116.     } while (fCurrentNode != fTail);            // as long as we are inside the linked list    
  117.  
  118.     return false;                                // nothing happened…
  119. OK:    return true;                                // OK, we were able to delete the entry
  120. }
  121.  
  122.  
  123. #pragma segment Collections
  124. Boolean TLinkedList::IsEmpty()
  125. // Check if the collection is empty (head points at tail).
  126. {
  127.     return (fHead->fNext == fTail);                // check if the next pointer is pointing on the tail
  128. }
  129.  
  130.  
  131. #pragma segment Collections
  132. Boolean TLinkedList::Find(const TItemtype item)
  133. // Find if we have the itemtype in the 'queue'.
  134. {
  135.     TItemtype tempVal;
  136.     this->Reset();                                // get back to the first entry
  137.  
  138.     while ((tempVal = this->Next()) != NULL)
  139.         // get all entries one at a time
  140.         {
  141.             if (tempVal == item)                // if we found a one-to-one relationship
  142.                 return true;                    // signal OK
  143.         }
  144.     return false;                                // otherwise signal false
  145. }
  146.  
  147.  
  148. #pragma segment Collections
  149. Size TLinkedList::GetSize() const
  150. // Return the internal count of entries in collection.
  151. {
  152.     return fCollectionSize;
  153. }
  154.  
  155.  
  156. // ITERATORS
  157. #pragma segment Collections
  158. TItemtype TLinkedList::First()
  159. // Get the first entry in the collection.
  160. {
  161.     if (fCollectionSize != 0)
  162.         return fHead->fNext->fKey;                // return what FHead->fNext has (the first 'real' node)
  163.     else
  164.         return NULL;
  165. }
  166.  
  167.  
  168. #pragma segment Collections
  169. TItemtype TLinkedList::Next()
  170. // Return next entry in the list.
  171. {
  172.     TItemtype item;
  173.     item = fCurrentNode->fKey;                    // get current item
  174.     fCurrentNode = fCurrentNode->fNext;            // then increase the node to the next entry
  175.  
  176.     return item;                                // return item
  177. }
  178.  
  179.  
  180. #pragma segment Collections
  181. TItemtype TLinkedList::Last()
  182. // Return last entry in the list.
  183. {
  184.     // because we cached the first entry, it's easy to return it
  185.     // we might have problems with a real de-queue later if 
  186.     // we don't revise this scheme (but most likely we will override Last() with new behavior
  187.     return fLastEntry->fKey;                    // the fLastNode is always pointing at the last entry
  188. }
  189.  
  190.  
  191. #pragma segment Collections
  192. void TLinkedList::Reset()
  193. // Make the current node point at the first entry from beginning (reset).
  194. {
  195.     fCurrentNode = fHead->fNext;                // reset the fCurrentNode pointer to the first entry
  196. }
  197.  
  198.  
  199. // _________________________________________________________________________________________________________ //
  200. //    TSTACK Class definitions.
  201.  
  202. //    CONSTRUCTORS AND DESTRUCTORS
  203. #pragma segment Collections
  204. TStack::TStack(short max)
  205. // Create a stack class (max pre-defined in the header files as a constant).
  206. {
  207.     fMaxCollectionSize = max;                    // keep track of how big we could grow the stack into
  208.     fCollectionSize = 0;                        // no entries yet
  209. }
  210.  
  211.  
  212. #pragma segment Collections
  213. TStack::~TStack()
  214. // Default constructor -- not used for the time being.
  215. {
  216. }
  217.  
  218.  
  219. //    MAIN INTERFACES
  220. #pragma segment Collections
  221. Boolean TStack::Push(const TItemtype item)
  222. // Push entry into the stack (beginning).
  223. {
  224.     if (fCollectionSize < fMaxCollectionSize)    // yes, we bail out and don't add more entries
  225.     {
  226.         struct fNODE* temp = new fNODE;            // create temp node
  227.         temp->fKey = item;                        // store value in temp node
  228.         temp->fNext = fHead->fNext;                // make temp point at the real next block 
  229.         fHead->fNext = temp;                    // and make temp suddenly the real head (stack head!)
  230.         fCollectionSize++;                        // increase the stack size
  231.         if (fCollectionSize == 1)                // first entry?
  232.             fLastEntry = temp;                    // store it for fast access later (cache the ptr)
  233.  
  234.         goto PushOK;
  235.     }
  236.     ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of the Stack");// signal about a possible problem
  237.     return false;
  238. PushOK:return true;
  239. }
  240.  
  241.  
  242. #pragma segment Collections
  243. TItemtype TStack::Pop()
  244. // Pop entry from the stack (beginning).
  245. {
  246.     TItemtype item;
  247.     struct fNODE* temp = fHead->fNext;            // create temp node with the header next pointer values
  248.     fHead->fNext = temp->fNext;                    // copy back the following pointer from temp to the head
  249.     item = temp->fKey;                            // get the temp item
  250.  
  251.     delete temp;                                // delete the suddenly non-needed node
  252.     fCollectionSize--;                            // decrease the stack
  253.     return item;                                // return the asked value
  254. }
  255.  
  256.  
  257. #pragma segment Collections
  258. Boolean TStack::Append(const TItemtype item)
  259. // Append entry, same as Push, but we included it.
  260. {
  261.     return (this->Push(item));                    // call the one and only method supported with stack insertion
  262. }
  263.  
  264.  
  265. #pragma segment Collections
  266. Boolean TStack::Remove(const TItemtype            /*item*/)
  267. // Remove is not implemented, but we had to include it because we are subclassing from a TLinkedList.
  268. {
  269.     return false;                                // not supported
  270. }
  271.  
  272.  
  273. // _________________________________________________________________________________________________________ //
  274. //    TQUEUE
  275. //    CONSTRUCTORS AND DESTRUCTORS
  276. #pragma segment Collections
  277. TQueue::TQueue(short max)
  278. // Create a queue class (max pre-defined in the header files as a constant).
  279. {
  280.     fMaxCollectionSize = max;                    // keep track of how big we could grow the stack into
  281.     fCollectionSize = 0;                        // no entries yet
  282. }
  283.  
  284.  
  285. #pragma segment Collections
  286. TQueue::~TQueue()
  287. // Default destructor, not used for the time being.
  288. {
  289. }
  290.  
  291.  
  292. //    MAIN INTERFACE
  293. #pragma segment Collections
  294. Boolean TQueue::Put(const TItemtype item)
  295. // Put entry into the queue (beginning).
  296. {
  297.     if (fCollectionSize < fMaxCollectionSize)    // no problems?
  298.     {
  299.         struct fNODE* temp = new fNODE;            // create a new node
  300.         temp->fKey = item;                        // add value to the node bucket    
  301.  
  302.         // place it in the beginning of the queue
  303.         temp->fNext = fHead->fNext;                // push it into the queue
  304.         fHead->fNext = temp;                    // now it's there
  305.  
  306.         // update the tail pointer
  307.         if (fCollectionSize == 0)                // first entry?
  308.         {
  309.             fTail->fPrevious = temp;            // hook fTail to the entry
  310.         }
  311.         temp->fPrevious = fHead;                // and hook it back to head
  312.         fLastEntry->fPrevious = temp;            // and as well as to the current first entry
  313.         fLastEntry = temp;                        // and mark it as the current first entry    
  314.  
  315.         fCollectionSize++;                        // increase the collection count
  316.         goto QueueOK;
  317.     }
  318.     ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of TQueue");
  319.     return false;
  320. QueueOK:return true;
  321. }
  322.  
  323.  
  324. #pragma segment Collections
  325. Boolean TQueue::Append(const TItemtype item)
  326. // Append is the same as Put.
  327. {
  328.     return (this->Put(item));                    // call TQueue.Put
  329. }
  330.  
  331.  
  332. #pragma segment Collections
  333. TItemtype TQueue::Get()
  334. // Get entry from the end of the list (queue).
  335. {
  336.     TItemtype item;
  337.  
  338.     // get the node and the value        
  339.     struct fNODE* temp = fTail->fPrevious;        // get the last before fTail
  340.     item = temp->fKey;                            // get the item    
  341.  
  342.  
  343.     // change the pointers
  344.     fTail->fPrevious = temp->fPrevious;            // hook fTail to the previous entry
  345.     temp->fPrevious->fNext = fTail;                // hook the node to tail as well    
  346.  
  347.     delete temp;                                // delete the entry    
  348.  
  349.     fCollectionSize--;                            // decrease the collection count    
  350.     return item;                                // return the item
  351. }
  352.  
  353.  
  354. #pragma segment Collections
  355. TItemtype TQueue::Last()
  356. // Get last entry from queue.
  357. {
  358.     return fTail->fPrevious->fKey;                // get the last entry
  359. }
  360.  
  361.  
  362. #pragma segment Collections
  363. Boolean TQueue::Remove(const TItemtype            /*item*/)
  364. // Remove is not supported, but we needed to include this because TQueue is subclassed from TLinkedList
  365. {
  366.     return false;                                // not supported
  367. }
  368.  
  369.  
  370.  
  371. // _________________________________________________________________________________________________________ //
  372. // TDEQUE
  373.  
  374. //    CONSTRUCTORS AND DESTRUCTORS
  375. #pragma segment Collections
  376. TDeque::TDeque(short max)
  377. // Create dequeue class (max pre-defined in the header files as a constant).
  378. {
  379.     fMaxCollectionSize = max;                    // keep track of how big we could grow the stack into
  380.     fCollectionSize = 0;                        // no entries yet
  381. }
  382.  
  383.  
  384. #pragma segment Collections
  385. TDeque::~TDeque()
  386. // Default destructor, not used for the time being.
  387. {
  388. }
  389.  
  390. #pragma segment Collections
  391. Boolean TDeque::Push(const TItemtype item)
  392. // Push entry into queue (beginning).
  393. {
  394.     return (TQueue::Put(item));                    // call inherited Put
  395. }
  396.  
  397.  
  398. #pragma segment Collections
  399. Boolean TDeque::PutAtEnd(const TItemtype item)
  400. // Place entry at end of the deque.
  401. {
  402.     if (fCollectionSize < fMaxCollectionSize)    // bail out if we reach the limit
  403.     {
  404.         struct fNODE*     temp = new fNODE;        // create temp node
  405.         temp->fKey = item;                        // store the value
  406.         temp->fNext = fTail;                    // point at tail
  407.         temp->fPrevious = fTail->fPrevious;        // make the backwards ptr hooked to the new entry
  408.         fTail->fPrevious->fNext = temp;            // and hook the old last one into this one 
  409.         fTail->fPrevious = temp;                // make the tail point at the new entry
  410.  
  411.         fCollectionSize++;                        // increase the counter
  412.         goto PutAtEndOK;
  413.     }
  414.     ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of the TDeque");
  415.     return false;
  416. PutAtEndOK:return true;
  417. }
  418.  
  419.  
  420. #pragma segment Collections
  421. TItemtype TDeque::Pop()
  422. // Pop entry from the queue end.
  423. {
  424.     TItemtype item;
  425.     struct fNODE* temp = fHead->fNext;            // hook to the beginning of the deque
  426.     fHead->fNext = temp->fNext;                    // hook head to the following node
  427.     temp->fNext->fPrevious = fHead;                // and the next one should point back at the head
  428.  
  429.     item = temp->fKey;                            // get the stored value
  430.     delete temp;                                // remove node
  431.     fCollectionSize--;                            // decrease the counter
  432.  
  433.     return item;                                // and return the item
  434. }
  435.  
  436.  
  437. // _________________________________________________________________________________________________________ //
  438. // THASHTABLE 
  439.  
  440. //    CONSTRUCTORS AND DESTRUCTORS
  441. #pragma Collections
  442. THashTable::THashTable()
  443. // Create a hash table class.
  444. {
  445.     // Clear the array bucket.
  446.     for (long i = 0; i < NBUCKETS; i++)
  447.         fBucket[i] = 0;
  448. }
  449.  
  450.  
  451. #pragma Collections
  452. THashTable::~THashTable()
  453. // Delete any memory structures required for the hashtable class.
  454. {
  455.     // Delete the entries in the array bucket.    
  456.     for (long i = 0; i < NBUCKETS; i++)
  457.     {
  458.         THashEntryPtr p = fBucket[i];            // get a ptr to the entry
  459.         while (p)
  460.             // while a valid ptr
  461.             {
  462.                 fBucket[i] = p->fNext;            // install next entry in array
  463.                 delete p;                        // delete the current node
  464.                 p = fBucket[i];                    // ptr back to the new value
  465.             }
  466.     }
  467. }
  468.  
  469.  
  470. //    MAIN INTERFACE
  471. #pragma segment Collections
  472. Boolean THashTable::Add(THashKey key,
  473.                         TItemtype value)
  474. // Add entries to the hash table.
  475. {
  476.     THashEntryPtr p;
  477.     THashKey whichItem = this->Hash(key);        // hash out the real value
  478.  
  479.     cout << " whichItem = " << whichItem << "\n";
  480.  
  481.     p = FindInBucket(fBucket[whichItem], key);
  482.     if (p)                                        // have already an entry?
  483.         goto problem;                            // signal the failure
  484.  
  485.     p = this->AddElement(key);                    // add our entry
  486.     p->fValue = value;                            // add the item value to the entry
  487.     return true;
  488.  
  489. problem:return false;
  490. }
  491.  
  492.  
  493. #pragma segment Collections
  494. Boolean THashTable::Remove(THashKey key)
  495. // Remove entries from the hash table.
  496. {
  497.     THashKey whichItem = this->Hash(key);        // hash out the real value
  498.     THashEntryPtr p = FindInBucket(fBucket[whichItem], key);
  499.     if (!p)                                        // no valid ptr?
  500.         goto problem;
  501.  
  502.     if (fBucket[whichItem] == p)
  503.         fBucket[whichItem] = p->fNext;            // connect to next node
  504.  
  505.     delete p;                                    // delete current node
  506.     return true;
  507.  
  508. problem:return false;
  509. }
  510.  
  511.  
  512.  
  513. #pragma segment Collections
  514. TItemtype THashTable::Find(THashKey key)
  515. // Find entries in the hash table.
  516. {
  517.     THashKey whichItem = this->Hash(key);        // hash out the real value
  518.     THashEntryPtr p = this->FindInBucket(fBucket[whichItem], key);
  519.     if (!p)
  520.         return 0;                                // return 0
  521.     else
  522.         return p->fValue;                        // return valid value
  523. }
  524.  
  525.  
  526. #pragma segment Collections
  527. void THashTable::MapCar(MapFun function)
  528. // Run a function on each entry in the hash table.
  529. {
  530.     for (long i = 0; i < NBUCKETS; i++)            // iterate through all the entries
  531.     {
  532.         THashEntryPtr p = fBucket[i];            // get the ptr to the entry
  533.  
  534.         while (p)
  535.             // while we have a nice value
  536.             {
  537.                 function(p);                    // call the function on p
  538.                 p = p->fNext;                    // get the next ptr
  539.             }
  540.     }
  541. }
  542.  
  543.  
  544. // PRIVATE INTERNAL FUNCTIONS
  545. #pragma Collection
  546. THashEntryPtr THashTable::AddElement(THashKey key)
  547. // Add an element to the internal structure.
  548. {
  549.     THashKey whichItem = this->Hash(key);
  550.     THashEntryPtr p = new THashEntry(key);
  551.  
  552.     p->fNext = fBucket[whichItem];                // get ptr to next entry
  553.     if (fBucket[whichItem])                        // OK?
  554.         fBucket[whichItem]->fPrevious = p;        // establish ptrs backwards
  555.  
  556.     fBucket[whichItem] = p;                        // as well as the current one
  557.  
  558.     return p;
  559. }
  560.  
  561.  
  562. #pragma Collections
  563. THashKey THashTable::Hash(THashKey key)
  564. // Hash out a key to be used with the buckets. Override for your own preferences.
  565. {
  566.     key &= kResourceIDMask;
  567.     return ((THashKey)(0x7FF & (key ^ (key >> 11))) % NBUCKETS);
  568.     // Note: if you write your own hash function, don't forget modula NBUCKETS to get
  569.     // down the size of the key so it hooks to the buckets.
  570. }
  571.  
  572.  
  573. #pragma Collections
  574. THashEntryPtr THashTable::FindInBucket(THashEntryPtr p,
  575.                                        THashKey key)
  576. // Find bucket where entry is located.
  577. {
  578.     while (p)
  579.         // valid pointer?
  580.         {
  581.             if (p->fKey == key)                    // hit?
  582.                 break;                            // found it!
  583.             p = p->fNext;                        // nope, continue cycling the list
  584.         }
  585.     return p;                                    // return found (not found) ptr
  586. }
  587.  
  588.  
  589. // _________________________________________________________________________________________________________ //
  590.  
  591.  
  592. /*    Change History (most recent last):
  593.   No        Init.    Date        Comment
  594.   1            khs        11/7/92        New file
  595.   2            khs        11/28/92    Added TLinkedList
  596.   3            khs        11/29/92    Added TQueue and TDeque
  597.   4            khs        1/14/93        Cleanup
  598. */
  599.